#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <dirent.h>

#define BUFSIZE 512 // 文件路径长度可以到255,BUFSIZE太小会报警告
#define TIMESIZE 64

char file_type(struct stat *s)
{
	switch (s->st_mode & S_IFMT) {
		case S_IFBLK:
			return 'b';
		case S_IFCHR:
			return 'c';
		case S_IFDIR:
			return 'd';
		case S_IFIFO:
			return 'p';
		case S_IFLNK:
			return 'l';
		case S_IFREG:
			return '-';
		case S_IFSOCK:
			return 's';
		default:
			return '?';
	}
}

char *file_perm(struct stat *s)
{
	int flags[] = {S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH};
	int i;
	static char perm[10] = {0};

	for (i = 0; i < 9; i++) {
		if (!(s->st_mode & flags[i])) {
			perm[i] = '-';
		} else {
			switch (i % 3) {
				case 0:
					perm[i] = 'w';
					break;
				case 1:
					perm[i] = 'r';
					break;
				case 2:
					perm[i] = 'x';
					break;
			}
		}
	}

	return perm;
}

long int file_nlink(struct stat *s)
{
	return s->st_nlink;
}

long int file_size(struct stat *s)
{
	return s->st_size;
}

char *file_user_name(uid_t uid)
{
	struct passwd *pw = getpwuid(uid);
	if (NULL == pw)
		return NULL;
	return pw->pw_name;
}

char *file_group_name(gid_t gid)
{
	struct group *gr = getgrgid(gid);
	if (NULL == gr)
		return NULL;
	return gr->gr_name;
}

char *file_time_string(time_t tim)
{
	static char timestr[TIMESIZE] = {'\0'};
	struct tm *local = localtime(&tim);
	
	strftime(timestr, TIMESIZE, "%m月 %d %H:%M", local);

	return timestr;
}

void print_file(char *name)
{
	struct stat s;

	char type;
	char *perm;
	long int nlink;
	char *uid;
	char *gid;
	long int size;
	char *timestr;

	stat(name, &s);

	type = file_type(&s);
	perm = file_perm(&s);
	nlink = file_nlink(&s);
	uid = file_user_name(s.st_uid);
	gid = file_group_name(s.st_gid);
	size = file_size(&s);
	timestr = file_time_string(s.st_mtim.tv_sec);

	printf("%c%s %ld %s %s %-8ld %s %s\n", type, perm, nlink, uid, gid, size, timestr, name);
}

int main(int argc, char *argv[])
{
	DIR *dp = NULL;
	struct dirent *entry = NULL;
	char dir_name[BUFSIZE];
	char opt;
	int i = 0;

	int long_format = 0; // 是否使用长格式打印

	// 读出所有选项
	while ((opt = getopt(argc, argv, "-l")) != -1) {
		switch (opt) {
			case 'l':
				long_format = 1;
				break;
		}
	}

	if (optind < argc) {
		strcpy(dir_name, argv[optind]); // 读完剩下的是非选项参数
	} else {
		getcwd(dir_name, BUFSIZE); // 获取当前工作路径
	}
	dp = opendir(dir_name);

	while (1) {
		entry = readdir(dp);
		if (NULL == entry) {
			if (errno) {
				perror("Error open entry");
				goto ERROR;
			}
			break;
		}
		if (strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0) {
			continue;
		}
		if (long_format) {
			print_file(entry->d_name);
		} else {
			printf("%s\t", entry->d_name);
			if ((++i) % 5 == 0) {
				printf("\n");
			}
		}
	}

	closedir(dp);
	return 0;
ERROR:
	closedir(dp);
	return 1;
}

